#include "OverlayManagement.h"
#include "BbsControlData.h"
#include "StringTable.h"
#include "Input.h"
#include "Output.h"
#include "Users.h"
#include "md5.h"
#include "Main.h"
#include "UserPortRS232.h"
#include "Global.h"
#include "Session.h"


#include <string.h>
#include <stdio.h>
#include <ctype.h>

#pragma code-name("LOGINCODE");
#pragma rodata-name("LOGINCODE");
#pragma data-name("LOGINDATA");
#pragma bss-name("LOGINBSS");

void proceed(void);
void newUser(void);
bool loginUser(char count, char maxcount, bool dcdStart,
	connect_states_t originalState);

void login()
{
	bool dcdStart = readDCD();
	connect_states_t originalConnectState = _connectState;
	static char c;
	static char buffer[6];

	if(!dcdStart || _connectState == CS_DISCONNECTED) return;

	newSession();

	outputCharacter(PLATFORM_ENCODING, OD_MODEM, 5);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_HIT_ENTER);
	input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA, buffer, 6, false);

	if(readDCD() == dcdStart && originalConnectState == _connectState)
	{
		for(c = 0; c < 6; ++c)
		{
			buffer[c] = tolower(buffer[c]);
		}

		if(strcmp(buffer, "putty") == 0)
		{
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_PUTTY);
			if(readDCD())
			{
				hangUpCall();
			}
			return;
		}

		c = 0;
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_ASK_PETSCII);
	
		while(c != 'y' && c != 'n'
			&& c != 'Y' && c != 'N'
			&& originalConnectState == _connectState)
		{
			c = inputChar(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA);
			if(readDCD() != dcdStart) return;
		}

		if(c == 'y' || c == 'Y') 
		{
			_currentSession.sessionEncoding = EN_PETSCII;
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_PETSCII_ON);
		}
		else if(c == 'n' || c == 'N')
		{
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_ASCII_ON);
		}
		else
		{
			hangUpCall();
			return;
		}

		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_BANNER_1);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_BANNER_2);

		outputFile(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), "login.open", &(bcd.dataDirectory));

		clearReadBuffer();

		if(loginUser(0, 5, dcdStart, _connectState))
		{
			proceed();
		}
		else
		{
			if(dcdStart != readDCD())
				outputString(PLATFORM_ENCODING, OD_SCREEN, CRLF CHX_RON CHX_RED "Connection Dropped.  Resetting BBS." CHX_ROFF CRLF);
			else
				outputString(PLATFORM_ENCODING, OD_SCREEN, CRLF CHX_RON CHX_RED "Login failed.  Resetting BBS." CHX_ROFF CRLF);
		}
	}
}

bool loginUser(char count, char maxcount, bool dcdStart, connect_states_t originalState)
{
	int usernumber;
	char buffer[81];
	
	if(readDCD() != dcdStart || originalState != _connectState) return false;

	if(count == maxcount) 
	{
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_MAX_ATTEMPTS);
		return false;
	}

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_NEW_USER);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_1);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_2);

	input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA_NUMERIC, buffer, 12, false);
	if(_connectState != CS_CONNECTED) return false;

	if(strcmp(buffer, "new") == 0)
	{
		//
		newUser();
		if(_connectState != CS_CONNECTED) return false;
		return loginUser(0, maxcount, dcdStart, originalState);
	}
	else if(strlen(buffer) == 0)
	{
		return loginUser(count + 1, maxcount, dcdStart, originalState);
	}
	else
	{
		usernumber = loadUserByName(buffer, &_currentUser);

		if(usernumber > 0)
		{
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_3);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_4);

			input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA_NUMERIC, buffer, 12, true);

			if(strlen(buffer) == 0)
			{
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_5);
				return loginUser(count + 1, maxcount, dcdStart, originalState);
			}

			if(memcmp(_currentUser.passwordHash, MDString(buffer), 16) == 0)
			{
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_5);
				return true;
			}
			else
			{
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_WRONG_PW);
				return loginUser(count + 1, maxcount, dcdStart, originalState);
			}
		}
		else
		{
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_5);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_UNKNOWN_USER);
			return loginUser(count + 1, maxcount, dcdStart, originalState);
		}
	}
	return false;
}

void loginSysop(void)
{
	loadUserByNumber(1, &_currentUser);
	proceed();
}

void newUser(void)
{
	static char buffer[21];
	static int c;
	bool passwordSet = false;

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CHX_CLEAR);
	outputFile(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), "login.app", &(bcd.dataDirectory));
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_1);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_2);
	memset(buffer, 0, 21);
	input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA_NUMERIC, buffer, 11, false);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
	if(strlen(buffer) == 0) 
	{
		// Good place to put a message.
		return;
	}
	strcpy(_currentUser.username, buffer);
	strcpy(_currentUser.decorated_username, buffer);

	while(!passwordSet)
	{
		if(_connectState != CS_CONNECTED) return;

		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_3);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_4);
		memset(buffer, 0, 21);
		input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA_NUMERIC, buffer, 20, false);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
		if(strlen(buffer) == 0)
		{
			// Good place to put a message;
			return;
		}

		if(strlen(buffer) < 5)
		{
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_PW_TOO_SHORT);
			pause(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM));
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), DOWN_AND_UP);
		}
		else
		{
			memcpy(_currentUser.passwordHash, MDString(buffer), 16);
			passwordSet = true;
		}
	}
		
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_5);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_6);
	memset(buffer, 0, 21);
	input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA_NUMERIC, buffer, 20, false);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
	strcpy(_currentUser.firstName, buffer);

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_7);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_8);
	memset(buffer, 0, 21);
	input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA_NUMERIC, buffer, 20, false);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
	strcpy(_currentUser.lastName, buffer);

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_9);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_10);
	memset(buffer, 0, 21);
	input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA_NUMERIC, buffer, 13, false);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
	strcpy(_currentUser.phone, buffer);

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_11);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_CONFIRM);

	c = 0;
	while(c != 'y' && c != 'Y' && c != 'n' && c != 'N')
	{
		c = inputChar(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA);
	}

	if(c == 'y' || c == 'Y')
	{
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_SAVING);
		_currentUser.usernumber = getNextUserNumber();
		c = saveUser(&_currentUser);
		if(c > 0)
		{
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_DONE);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_NEW);
			sprintf(buffer, "%u", c);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
		}
		else
		{
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_APP_FAILED);
			sprintf(buffer, CHX_RED "ERROR CODE: %d", c);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
		}
	}

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);

}

void proceed(void)
{
	if(_connectState != CS_CONNECTED) return;

	bcd.subsInitialized = false;

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CHX_CLEAR);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), LOGIN_FORM_WELCOME);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), _currentUser.decorated_username);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), "!" CRLF);

	pause(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM));

	loadOverlay("bbs.main", &loggedIn, "bbs.login");
}
